﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reactive;
using System.Threading.Tasks;
using Avalonia.Threading;
using MilkTeaMachine.OfflineClient.Utils;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;

namespace MilkTeaMachine.OfflineClient.ViewModels;

public class WeightingCalibrationViewModel : ViewModelBase, IRoutableViewModel
{
    private readonly byte _weightingAddress;

    public WeightingCalibrationViewModel(IScreen hostScreen, byte weightingAddress)
    {
        HostScreen = hostScreen;
        SaveAndQuitCmd =
            ReactiveCommand.CreateFromTask(SaveAndQuitAsync,
                this.WhenAnyValue(
                    x => x.CalibrationFinished,
                    (bool @bool) => @bool));
        GoBackCmd = ReactiveCommand.CreateFromObservable(() =>
            HostScreen.Router.NavigateBack.Execute());
        CalibrationWeightCmd =
            ReactiveCommand.CreateFromTask(async (int weight) => await CalibrationWeightAsync(weight));
        CalibrationFinishedCmd = ReactiveCommand.Create(() => { CalibrationFinished = true; });
        _weightingAddress = weightingAddress;
        GetWeightCmd = ReactiveCommand.CreateFromTask(async () => await GetWeightAsync(0));
        Task.Run(() =>
        {
            while (true)
            {
                Dispatcher.UIThread.InvokeAsync(async () =>
                    CurrentWeight = (await GetWeightAsync(0)).ToString(CultureInfo.InvariantCulture));
                Task.Delay(200).GetAwaiter().GetResult();
            }
        });
    }

    public WeightingCalibrationViewModel() : this(default!, 0x01)
    {
    }

    private async Task CalibrationWeightAsync(int weight)
    {
        if (Finish200 && Finish100 && Finish50 && Finish10 && Finish500)
        {
            CalibrationFinished = true;
        }

        _ = await GetWeightAsync(weight).ConfigureAwait(true);

        switch (weight)
        {
            case 10:
                Finish10 = true;
                break;
            case 50:
                Finish50 = true;
                break;
            case 100:
                Finish100 = true;
                break;
            case 200:
                Finish200 = true;
                break;
            case 500:
                Finish500 = true;
                break;
        }
    }

    public ReactiveCommand<Unit, double> GetWeightCmd { get; }

    private async Task<double> GetWeightAsync(int num)
    {
#if DEBUG
        var value = new Random().NextDouble() * 10;
        _weightDictionary[10] = value;
        _weightDictionary[50] = value * 5;
        _weightDictionary[100] = value * 10;
        _weightDictionary[200] = value * 20;
        _weightDictionary[500] = value * 50;
        return value;
#endif
        using var readWeightMaster = IModbusSerialFactory.Create(9600);
        var weight = await readWeightMaster.ReadHoldingRegistersAsync(_weightingAddress, 0x01, 2);
        if (weight is null)
        {
            _ = Dispatcher.UIThread.InvokeAsync(async () =>
            {
                await MessageBoxUtil.GetBox("读取称重失败").ShowWindowAsync().ConfigureAwait(true);
            });
            return 0.00;
        }

        var highBits = BitConverter.GetBytes(weight[0]);
        var lowBits = BitConverter.GetBytes(weight[1]);
        var weightValue = BitConverter.ToInt32([..lowBits, ..highBits]);
        var thisWeight = Math.Round(weightValue / 10.0, 1);
        switch (num)
        {
            case 10:
                _weightDictionary[10] = thisWeight;
                break;
            case 50:
                _weightDictionary[50] = thisWeight;
                break;
            case 100:
                _weightDictionary[100] = thisWeight;
                break;
            case 200:
                _weightDictionary[200] = thisWeight;
                break;
            case 500:
                _weightDictionary[500] = thisWeight;
                break;
        }

        return thisWeight;
    }

    private readonly Dictionary<int, double> _weightDictionary = new(5);

    private Task SaveAndQuitAsync()
    {
        _weight = _weightDictionary.Sum(x => x.Value);
        var parser = new IniFileParser.IniFileParser();
        const string path = "Configuration/config.ini";
        var data = parser.ReadFile(path);
        data["Weight"]["Multiple"] =
            Math.Round(_weight / (10 + 50 + 100 + 200 + 500), 1).ToString(CultureInfo.InvariantCulture);
        parser.WriteFile(path, data);
        HostScreen.Router.NavigateAndReset.Execute(new MainPageViewModel(HostScreen));
        return Task.CompletedTask;
    }

    public string? UrlPathSegment { get; } = nameof(WeightingCalibrationViewModel);
    public IScreen HostScreen { get; }
    [Reactive] private bool CalibrationFinished { get; set; }
    [Reactive] private bool Finish10 { get; set; }
    [Reactive] private bool Finish50 { get; set; }
    [Reactive] private bool Finish100 { get; set; }
    [Reactive] private bool Finish200 { get; set; }
    [Reactive] private bool Finish500 { get; set; }
    [Reactive] public string CurrentWeight { get; set; } = "22.33";
    private double _weight;
    public ReactiveCommand<Unit, Unit> SaveAndQuitCmd { get; }
    public ReactiveCommand<Unit, IRoutableViewModel> GoBackCmd { get; }
    public ReactiveCommand<int, Unit> CalibrationWeightCmd { get; }
    public ReactiveCommand<Unit, Unit> CalibrationFinishedCmd { get; }
}